/*
 * Copyright (c) 2005-2007 Terracotta, Inc. All rights reserved.
 */
package org.terracotta.workmanager.routing;

import java.util.Date;

import org.terracotta.workmanager.DefaultWorkEvent;
import org.terracotta.workmanager.MutableWorkItem;
import org.terracotta.workmanager.routing.Routable;
import org.terracotta.workmanager.routing.RoutableWorkItem;

import commonj.work.Work;
import commonj.work.WorkEvent;
import commonj.work.WorkException;
import commonj.work.WorkListener;

/**
 * The work item, holds the work to be executed, status of the progress and the
 * future result.
 * <p>
 * This class is an implementation of the <code>OptimizableWorkItem</code>
 * interface which provides optional cancellation of the tracking of work status
 * that is defined by the CommonJ spec, which means that the worker will *not*
 * track work status. This this is the preferred WorkItem to use and will enable
 * much better performance and scalability while sacrificing control and
 * spec-compliance.
 */
public class RoutableWorkItem<ID> implements Routable<ID>, MutableWorkItem {

  public static int CANCEL_WORK_STATUS_TRACKING = 1 << WorkEvent.WORK_STARTED;

  protected final int flags;

  protected final Work work;

  protected transient final WorkListener workListener;

  protected int status;

  protected boolean dirty;

  protected WorkException m_exception;

  protected ID routingID;
  
  protected final Date timeCreated;

  public RoutableWorkItem(
      final int flags, final Work work, final WorkListener workListener, final ID routingID) {
    this.flags = flags;
    this.work = work;
    this.status = -1;
    this.dirty = false;
    this.workListener = workListener;
    this.routingID = routingID;
    this.timeCreated = new Date();
  }

  public RoutableWorkItem(final Work work, final WorkListener workListener, final ID routingID) {
    this.flags = 0;
    this.work = work;
    this.status = -1;
    this.dirty = false;
    this.workListener = workListener;
    this.routingID = routingID;
    this.timeCreated = new Date();
  }

  public Work getResult() {    
    return work;
  }

  public int getFlags() {
    return flags;
  }

  public synchronized int getStatus() {
    return status;
  }
  
  public Date getTimeCreated() {
    return timeCreated;
  }

  public synchronized void setStatus(final int status, final WorkException exception) {
    this.status = status;
    this.m_exception = exception;
    dirty = true;
  }

  public synchronized ID getRoutingID() {
    return routingID;
  }

  public synchronized void setRoutingID(final ID routingID) {
    this.routingID = routingID;
  }

  public WorkListener getWorkListener() {
    return workListener;
  }

  public void fireListener() {
    if (workListener == null) {
      return;
    }

    int status;
    WorkException exception;
    synchronized (this) {
      if (!dirty) {
        return;
      }
      status = getStatus();
      exception = m_exception;
      dirty = false;
    }

    switch (status) {
      case WorkEvent.WORK_ACCEPTED:
        workListener.workAccepted(new DefaultWorkEvent(WorkEvent.WORK_ACCEPTED, this, exception));
        break;
      case WorkEvent.WORK_REJECTED:
        workListener.workRejected(new DefaultWorkEvent(WorkEvent.WORK_REJECTED, this, exception));
        break;
      case WorkEvent.WORK_STARTED:
        workListener.workStarted(new DefaultWorkEvent(WorkEvent.WORK_STARTED, this, exception));
        break;
      case WorkEvent.WORK_COMPLETED:
        workListener.workCompleted(
            new DefaultWorkEvent(WorkEvent.WORK_COMPLETED, this, exception));
        break;        
      case WorkEvent.WORK_NOOS:
        workListener.workCompleted(new DefaultWorkEvent(WorkEvent.WORK_NOOS, this, exception));
        break;
    }
  }

  public WorkException newWorkException(final Throwable e) {
    e.printStackTrace();
    WorkException we = new WorkException(e.getMessage());
    setStatus(WorkEvent.WORK_REJECTED, we);
    Thread.currentThread().interrupt();
    return we;
  }

  @Override
  public synchronized String toString() {
    String status;
    switch (this.status) {
      case -1:
        status = "WORK_CREATED";
        break;
      case WorkEvent.WORK_ACCEPTED:
        status = "WORK_ACCEPTED";
        break;
      case WorkEvent.WORK_COMPLETED:
        status = "WORK_COMPLETED";
        break;
      case WorkEvent.WORK_REJECTED:
        status = "WORK_REJECTED";
        break;
      case WorkEvent.WORK_STARTED:
        status = "WORK_STARTED";
        break;
      case WorkEvent.WORK_NOOS:
        status = "WORK_NOOS";
        break;
      case WorkEvent.WORK_SCHEDULED:
        status = "WORK_SCHEDULED";
        break;          
      default:
        throw new IllegalStateException("illegal (unknown) status " + this.status);
    }
    return status;
  }

  public static class Factory<ID> {
    private final int flags;

    public Factory() {
      this(0);
    }

    public Factory(final int flags) {
      this.flags = flags;
    }

    public RoutableWorkItem<ID> create(Work work, WorkListener listener, ID routingID) {
      return new RoutableWorkItem<ID>(flags, work, listener, routingID);
    }
  }
}
